﻿using System;
using System.IO;

namespace System.IO
{
    public class Base64Decoder : Stream
    {
        readonly TextReader reader;
        char[] inbuf = new char[1024]; // must be multiple of 4 chars
        byte[] buf; 
        int pos;
        int last;
        long length;

        public Base64Decoder(TextReader reader)
        {
            this.reader = reader;
            last = 10;
            pos = last;
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            int i;
            for(i=0; i<count; i++)
            {
                int c = read();
                if(c < 0)
                {
                    if(i == 0)
                        return 0;
                    else
                        return i;
                }    

                buffer[offset + i] = (byte)c;
            }
            return i;
        }

        private int read()
        {
            if(pos == last)
            {
                int c = readMultipleOfFour(inbuf, inbuf.Length);
                if(c < 0)
                    return c;
                
                buf = Convert.FromBase64CharArray(inbuf, 0, c);
                
                last = buf.Length;
                pos = 0;

                length += buf.Length;
            }
            
            return buf[pos++];            
        } 

        private int readMultipleOfFour(char[] buffer, int count)
        {
            int i = 0;
            while(i < count)
            {
                int ch = reader.Read();
                if(ch < 0)
                {
                    if(i == 0)
                        return ch;
                    else
                        return i;
                }    
                
                if(IsBase64Char(ch))
                    buffer[i++] = (char)ch;

            }
            return i;
        }

        public static bool IsBase64Char(int ch)
        {
            bool yes = (ch >= 'A' && ch <= 'Z') 
                    || (ch >= 'a' && ch <= 'z')  
                    || (ch >= '0' && ch <= '9')  
                    || (ch == '/')  
                    || (ch == '+')  
                    || (ch == '=');
            return yes;          
        }

        protected override void Dispose(bool disposing)
        {
            if(disposing)
            {
                reader.Close();
            }
            base.Dispose(disposing);
        }

#region other trivial abstract methods

        public override void Flush()
        {
            throw new NotImplementedException();
        }

        public override bool CanRead
        {
            get { return true; }
        }

        public override bool CanSeek
        {
            get { return false; }
        }

        public override bool CanWrite
        {
            get { return false; }
        }

        public override long Length
        {
            get { return length; }
        }

        public override long Position
        {
            get
            {
                return length;
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            throw new NotImplementedException();
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            throw new NotImplementedException();
        }

        public override void SetLength(long value)
        {
            throw new NotImplementedException();
        }
#endregion

    }
}
